package cn.walls1717.wallsmall.member.service.impl;

import cn.hutool.core.util.RandomUtil;
import cn.walls1717.wallsmall.common.utils.PageUtils;
import cn.walls1717.wallsmall.common.utils.Query;
import cn.walls1717.wallsmall.member.dao.MemberDao;
import cn.walls1717.wallsmall.member.entity.MemberEntity;
import cn.walls1717.wallsmall.member.entity.MemberLevelEntity;
import cn.walls1717.wallsmall.member.exception.PhoneExistException;
import cn.walls1717.wallsmall.member.exception.UsernameExistException;
import cn.walls1717.wallsmall.member.service.MemberLevelService;
import cn.walls1717.wallsmall.member.service.MemberService;
import cn.walls1717.wallsmall.member.vo.MemberLoginVO;
import cn.walls1717.wallsmall.member.vo.MemberRegisterVO;
import cn.walls1717.wallsmall.member.vo.SocialUserVO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;

import static cn.walls1717.wallsmall.common.constant.auth.AuthServerConstant.USERNAME_PREFIX;

@Service("memberService")
public class MemberServiceImpl extends ServiceImpl<MemberDao, MemberEntity> implements MemberService {
    @Resource
    private MemberLevelService memberLevelService;
    @Resource
    private ThreadPoolExecutor executor;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<MemberEntity> page =
            this.page(new Query<MemberEntity>().getPage(params), new QueryWrapper<MemberEntity>());

        return new PageUtils(page);
    }

    /**
     * 保存用户信息
     *
     * @param registerVO 注册数据
     */
    @Override
    public void saveMember(MemberRegisterVO registerVO) {
        String phone = registerVO.getPhone();
        String username = registerVO.getUsername();
        checkUsernameUnique(username);
        checkPhoneUnique(phone);

        MemberEntity member = new MemberEntity();
        CompletableFuture<Void> levelFuture = CompletableFuture.runAsync(() -> {
            MemberLevelEntity memberLevel = memberLevelService.getDefaultLevel();
            member.setLevelId(memberLevel.getId());
        }, executor);

        // md5盐加密
        CompletableFuture<Void> passwordFuture = CompletableFuture.runAsync(() -> {
            BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
            String password = passwordEncoder.encode(registerVO.getPassword());
            member.setPassword(password);
        }, executor);

        member.setMobile(phone);
        member.setUsername(username);
        member.setNickname(username);

        try {
            CompletableFuture.allOf(levelFuture, passwordFuture).get();
        } catch (Exception e) {
            e.printStackTrace();
        }
        save(member);

    }

    /**
     * 根据用户名与密码获取用户信息
     *
     * @param loginVO 用户名，密码
     * @return 用户信息
     */
    @Override
    public MemberEntity getMemberByNameAndPassword(MemberLoginVO loginVO) {
        String account = loginVO.getLoginAccount();
        MemberEntity member =
            lambdaQuery().eq(MemberEntity::getUsername, account).or().eq(MemberEntity::getMobile, account).one();
        if (member != null) {
            String encodePassword = member.getPassword();
            BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
            boolean matches = passwordEncoder.matches(loginVO.getPassword(), encodePassword);
            if (!matches) {
                return null;
            }

            return member;
        }
        return null;
    }

    /**
     * 第三方登录
     *
     * @param loginVO 登录参数
     * @return 结果
     */
    @Override
    public MemberEntity socialLogin(SocialUserVO loginVO) throws ExecutionException, InterruptedException {
        String socialId = loginVO.getId();
        // 根据socialId获取，能获取到说明已经用此方式登陆过，直接返回信息即可。
        MemberEntity member = lambdaQuery().eq(MemberEntity::getSocialId, socialId).one();
        if (member != null) {
            return member;
        }

        MemberEntity newMember = new MemberEntity();
        CompletableFuture<Void> levelFuture = CompletableFuture.runAsync(() -> {
            MemberLevelEntity memberLevel = memberLevelService.getDefaultLevel();
            newMember.setLevelId(memberLevel.getId());
        }, executor);

        CompletableFuture<Void> passwordFuture = CompletableFuture.runAsync(() -> {
            // 设置随机的密码
            String password = RandomUtil.randomString(10);
            BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
            String encodePassword = passwordEncoder.encode(password);
            newMember.setPassword(encodePassword);
        }, executor);

        String name = loginVO.getName();
        newMember.setUsername(name);
        newMember.setNickname(name);
        newMember.setSocialId(socialId);

        // 默认使用第三方的name作为username，但是username已经存在则设置为随机值。
        try {
            checkUsernameUnique(name);
        } catch (Exception e) {
            // 设置用户名为随机值
            newMember.setUsername(USERNAME_PREFIX + RandomUtil.randomString(10));
            newMember.setNickname(USERNAME_PREFIX + RandomUtil.randomString(10));
        }

        CompletableFuture.allOf(levelFuture, passwordFuture).get();

        // 保存用户信息
        save(newMember);
        return newMember;
    }

    /**
     * 校验用户名是否已存在
     *
     * @param username 用户名
     */
    private void checkUsernameUnique(String username) {
        Long usernameCount = lambdaQuery().eq(MemberEntity::getUsername, username).count();
        if (usernameCount > 0) {
            throw new UsernameExistException();
        }
    }

    /**
     * 校验手机号是否已存在
     *
     * @param phone 手机号
     */
    private void checkPhoneUnique(String phone) {
        Long phoneCount = lambdaQuery().eq(MemberEntity::getMobile, phone).count();
        if (phoneCount > 0) {
            throw new PhoneExistException();
        }
    }

}